package com.ctg.itrdc.imageconsole.service.impl;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.ctg.itrdc.imageconsole.common.constants.DockerConstant;
import com.ctg.itrdc.imageconsole.common.constants.Path;
import com.ctg.itrdc.imageconsole.common.util.DirUtils;
import com.ctg.itrdc.imageconsole.common.util.TimeUtils;
import com.ctg.itrdc.imageconsole.data.dao.IDockerImageDao;
import com.ctg.itrdc.imageconsole.data.dao.IDockerImageTagDao;
import com.ctg.itrdc.imageconsole.data.dao.IDockerMicroImageDao;
import com.ctg.itrdc.imageconsole.data.dao.IDockerMicroUserDao;
import com.ctg.itrdc.imageconsole.data.dto.DockerMicroImageContentDTO;
import com.ctg.itrdc.imageconsole.data.dto.DockerMicroImageDTO;
import com.ctg.itrdc.imageconsole.data.entity.DockerMicroImage;
import com.ctg.itrdc.imageconsole.data.entity.DockerMicroImageContent;
import com.ctg.itrdc.imageconsole.data.entity.user.DockerMicroUser;
import com.ctg.itrdc.imageconsole.servcie.user.ConfigReader;
import com.ctg.itrdc.imageconsole.service.IImageBuildService;
import com.ctg.itrdc.imageconsole.util.ShellUtils;
import com.ctg.itrdc.imageconsole.util.SysManagerUtil;
import com.ctg.itrdc.pasp.security.utils.WebUtils;
import com.ctg.itrdc.paspsdk.common.entity.PageInfo;
import com.ctg.itrdc.paspsdk.common.utils.type.StringUtils;

/**
 * 
 * @description: 保存构建记录--》返回给前端--》镜像构建--》更新数据
 * @version: v1.0.0
 * @author: Piao.Wu
 * 
 *          Modification History: Date Author Version Description
 *          -----------------------------------------------------------------
 *          2016年10月24日 上午11:42:17 Piao.Wu v1.0.0 create
 *
 */
@Component("imageBuildService")
public class ImageBuildServiceImpl implements IImageBuildService {

	private static final Logger logger = LoggerFactory.getLogger(ImageBuildServiceImpl.class);


	@Resource
	private IDockerMicroImageDao dockerMicroImageDao;
	@Resource
	private IDockerImageTagDao dockerImageIndexDao;
	@Resource
	private IDockerImageDao dockerImageFileDao;
	@Resource 
	IDockerMicroUserDao microUserDao;

	@Override
	public PageInfo<DockerMicroImage> queryBuildList(DockerMicroImage dockerMicroImage, Integer pageNum,
			Integer pageSize) throws Exception {

		boolean isDockerManager = SysManagerUtil.isRoleOfDockerManager();
		
		PageInfo<DockerMicroImage> pageInfo = dockerMicroImageDao.queryBuildList(dockerMicroImage, pageNum,
				pageSize, isDockerManager);
		if (pageInfo.getList() != null && pageInfo.getList().size() > 0) {
			for (DockerMicroImage microImage : pageInfo.getList()) {
				// TODO 这里是由于前端那边的要求：要求后端接收与返回都是以时间撮的形式。有问题联系前端
				if (StringUtils.isNotEmpty(microImage.getCreateTime())) {
					Long time = TimeUtils.parse(microImage.getCreateTime()).getTime();
					microImage.setCreateTime(time.toString());
				}
				if (StringUtils.isNotEmpty(microImage.getLastUpdateTime())) {
					Long time = TimeUtils.parse(microImage.getLastUpdateTime()).getTime();
					microImage.setLastUpdateTime(time.toString());
				}
			}
		}
		return pageInfo;
	}

	private String createCmd(String scriptName, DockerMicroImageDTO dockerMicroImageDTO) {
		String repositoryUrl = ConfigReader.readHarborVip();
		return "sh " + Path.BUILD_PATH + scriptName + " " + dockerMicroImageDTO.getImageName() + " "
				+ dockerMicroImageDTO.getTag() + " " + dockerMicroImageDTO.getSysUserCode() + " " + repositoryUrl;
	}

	private String createLogPath(String sysUserCode) {
		logger.info("日志文件存放路径：{}",Path.BUILD_LOG_PATH + sysUserCode + "/");
		return Path.BUILD_LOG_PATH + sysUserCode + "/";
	}

	private String createLogFileName(DockerMicroImageDTO dockerMicroImageDTO) {
		return dockerMicroImageDTO.getImageName() + "-" + dockerMicroImageDTO.getTag() + ".log";
	}

	@Override
	public Boolean executorImageBuild(final DockerMicroImageDTO dockerMicroImageDTO) {
		// 构建前保存构建记录到数据库中，状态为构建中
		final Long microImageId = this.saveToDb(dockerMicroImageDTO);
		if (microImageId == null) {
			return false;
		}
		try {
			Executors.newSingleThreadExecutor().submit(new Runnable() {
				@Override
				public void run() {
					logger.info("数据保存完毕！开始异步执行镜像构建...");
					startBuild(dockerMicroImageDTO, microImageId);
				}
			});
		} catch (Exception e) {
			logger.error("构建失败", e);
			return false;
		}
		return true;
	}

	private String startBuild(DockerMicroImageDTO dockerMicroImageDTO, Long microImageId) {
		boolean isSuccess = this.writeToShellFile(dockerMicroImageDTO.getDockerfile());
		if (!isSuccess) {
			this.updateStatusAfterFail(microImageId);
			logger.error("将dockerfile写入到临时脚本时出错！");
			return "将dockerfile写入到临时脚本时出错！";
		}

		String result = ShellUtils.ExeShell(createCmd("startbuildimage.sh", dockerMicroImageDTO),
				createLogPath(WebUtils.getCtgUser().getSysUserCode()), createLogFileName(dockerMicroImageDTO), true);
		if (result != null && result.endsWith("END")) {
			this.updateDataAfterSuccess(microImageId);
			this.deleteFilesAfterSuccess(dockerMicroImageDTO);
			logger.info("Build Success");
			return "Build Success";
		} else {
			this.updateStatusAfterFail(microImageId);
			logger.info("执行构建dockerfile出错!");
			return "执行构建dockerfile出错!";
		}
	}

	/**
	 * 
	 * @Description: 将dockerfile内容写入到临时的shell脚本中
	 * @author Piao.Wu 2016年10月24日 上午11:43:40
	 * @param dockerfile
	 * @return boolean
	 *
	 */
	private boolean writeToShellFile(String dockerfile) {
		boolean isSuccess = false;
		String tmpDockerFilePath = Path.BUILD_PATH + "Dockerfile";
		FileWriter fw = null;
		try {
			tmpDockerFilePath = URLDecoder.decode(tmpDockerFilePath, "UTF-8");
			File file = new File(tmpDockerFilePath);
			fw = new FileWriter(file, false); // 覆盖写入
			fw.write(dockerfile);
			isSuccess = true;
		} catch (Exception e) {
			logger.error("将dockerfile写入到shell脚本时出错！", e);
		} finally {
			try {
				if (fw != null) {
					fw.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return isSuccess;
	}

	private Long saveToDb(DockerMicroImageDTO dockerMicroImageDTO) {
		Long userId = WebUtils.getCtgUser().getSysUserId();
		String userCode = WebUtils.getCtgUser().getSysUserCode();
		DockerMicroImage dockerMicroImage = new DockerMicroImage();
		dockerMicroImage.setImageName(userCode + "/" + dockerMicroImageDTO.getImageName());
		dockerMicroImage.setTag(dockerMicroImageDTO.getTag());
		dockerMicroImage.setDockerfile(dockerMicroImageDTO.getDockerfile());
		dockerMicroImage.setBuildImageStatus(DockerConstant.DockerMicroImageCons.BUILDING);
		dockerMicroImage.setCreateUserId(userId);
		dockerMicroImage.setLastUpdateUserId(userId);
		dockerMicroImage.setCreateTime(TimeUtils.getNowDateStr());
		dockerMicroImage.setLastUpdateTime(TimeUtils.getNowDateStr());
		Long id = dockerMicroImageDao.insert(dockerMicroImage);

		for (DockerMicroImageContentDTO contentDTO : dockerMicroImageDTO.getImageContents()) {
			DockerMicroImageContent imageContent = new DockerMicroImageContent();
			imageContent.setDockerMicroImageId(id);
			imageContent.setUploadFile(contentDTO.getFileName());
			imageContent.setContainerPath(contentDTO.getContainerPath());
			imageContent.setCreateUserId(userId);
			imageContent.setLastUpdateUserId(userId);
			imageContent.setCreateTime(TimeUtils.getNowDateStr());
			imageContent.setLastUpdateTime(TimeUtils.getNowDateStr());
			dockerMicroImageDao.insert(imageContent);
		}
		return id;
	}


	private void updateDataAfterSuccess(Long microImageId) {
		DockerMicroImage dockerMicroImage = new DockerMicroImage();
		dockerMicroImage.setId(microImageId);
		dockerMicroImage = dockerMicroImageDao.selectByPrimaryKey(dockerMicroImage);

		dockerMicroImage.setBuildImageStatus(DockerConstant.DockerMicroImageCons.BUILD_SUCCESS);
		dockerMicroImage.setLastUpdateTime(TimeUtils.getNowDateStr());
		dockerMicroImageDao.updateByPrimaryKey(dockerMicroImage);

	}

	private void deleteFilesAfterSuccess(DockerMicroImageDTO dockerMicroImageDTO) {
		try {
			String fileDirPath = Path.BUILD_FILE_PATH + dockerMicroImageDTO.getSysUserCode() + "/"
					+ dockerMicroImageDTO.getTimestampDirectory() + "/";
			fileDirPath = URLDecoder.decode(fileDirPath, "UTF-8");
			DirUtils.deleteDir(new File(fileDirPath));
		} catch (Exception e) {
			logger.error("镜像构建成功后删除上传的文件，出现异常!", e);
		}
	}

	private void updateStatusAfterFail(Long microImageId) {
		DockerMicroImage dockerMicroImage = new DockerMicroImage();
		dockerMicroImage.setId(microImageId);
		dockerMicroImage = dockerMicroImageDao.selectByPrimaryKey(dockerMicroImage);
		dockerMicroImage.setBuildImageStatus(DockerConstant.DockerMicroImageCons.BUILD_FAIL);
		dockerMicroImage.setLastUpdateTime(TimeUtils.getNowDateStr());
		dockerMicroImageDao.updateByPrimaryKey(dockerMicroImage);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Map<String, Object> queryBuildLog(Long dockerMicroImageId, Long lastTimeFileSize) {
		DockerMicroImage dockerMicroImage = new DockerMicroImage();
		dockerMicroImage.setId(dockerMicroImageId);
		dockerMicroImage = dockerMicroImageDao.selectByPrimaryKey(dockerMicroImage);
		
		DockerMicroUser microUser = microUserDao.selectBySysUserId(dockerMicroImage.getLastUpdateUserId());
		
		String logPath = createLogPath(microUser.getCode());
		String imageName = dockerMicroImage.getImageName();
		imageName = imageName.substring(imageName.indexOf("/"), imageName.length());
		String logFileName = imageName + "-" + dockerMicroImage.getTag() + ".log";
		String logFile = logPath + logFileName;
		File file = new File(logFile);
		Map<String, Object> map = new HashMap<String, Object>();
		if (file.exists()) {
			Map<String, Object> logMap = getLog(logFile, lastTimeFileSize);
			List<String> logList = (List<String>) logMap.get("logList");
			if (logList == null || logList.size() == 0) {
				map.put("lastTimeFileSize", lastTimeFileSize);
				map.put("log", "");
				map.put("isEnd", false);
			} else {
				Long last = (Long) logMap.get("length");
				map.put("lastTimeFileSize", last);
				map.put("log", logList);
				map.put("isEnd", false);
				if (logList.size() > 2 && (logList.get(logList.size() - 1).contains("END")
						|| logList.get(logList.size() - 2).contains("END"))) {
					map.put("isEnd", true);
				}
			}
		} else {
			map.put("lastTimeFileSize", lastTimeFileSize);
			map.put("log", "您访问的日志不存在！");
			map.put("isEnd", true);
		}
		return map;
	}

	/**
	 * 根据日志类型，获取从上次文件大小的位置开始的日志内容
	 * 
	 * @param filePath
	 *            文件路径
	 * @param lastTimeFileSize
	 *            上次文件大小
	 * @return 日志数据
	 */
	public Map<String, Object> getLog(String filePath, Long lastTimeFileSize) {
		Map<String, Object> map = new HashMap<>();
		List<String> logList = new ArrayList<>();
		RandomAccessFile randomFile = null;
		try {
			randomFile = new RandomAccessFile(filePath, "r");
			randomFile.seek(lastTimeFileSize);

			String tmp = "";
			while ((tmp = randomFile.readLine()) != null) {
				tmp = new String(tmp.getBytes("ISO-8859-1"), "UTF-8");
				logList.add(tmp);
			}
			lastTimeFileSize = randomFile.length();
			map.put("logList", logList);
			map.put("length", lastTimeFileSize);
		} catch (Exception e) {
			logger.error("读取日志异常", e);
		} finally {
			if (randomFile != null) {
				try {
					randomFile.close();
				} catch (IOException e) {
					logger.error("读取日志时关闭流出现异常", e);
				}
			}
		}
		return map;
	}

	@Override
	public boolean isVersionExist(String imageName, String version) {
		String userCode = WebUtils.getCtgUser().getSysUserCode();  
		Long userId = WebUtils.getCtgUser().getSysUserId();
		imageName = userCode + "/" + imageName;
		
		List<DockerMicroImage> microImages = dockerMicroImageDao.isVersionExist(userId, imageName, version);

		return microImages != null && microImages.size()>0;
	}

	@Override
	public void deleteBuildRecord(DockerMicroImageDTO dockerMicroImageDTO) {
		dockerMicroImageDao.deleteById(dockerMicroImageDTO.getId());
	}

}
